﻿using ExcelUnity.Core;
using ExcelUnity.Exporter.Exporters;
using ExcelUnity.Exporter.Wrappers;
using ExcelUnity.Exporter.Wrappers.SheetWrapperBuilders;
using NPOI;
using NPOI.OpenXml4Net.OPC;
using NPOI.SS.UserModel;
using NPOI.SS.Util;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;

namespace ExcelUnity.Exporter
{
    public class DefaultExcelExporter : IExcelExporter
    {
        public void Export(ExportBook book, string filePath)
        {
            using var outStream = new FileStream(filePath, FileMode.Create, FileAccess.Write);
            Export(book, outStream);
        }

        public void Export(ExportBook exportBook, Stream stream)
        {
            if (stream == null) throw new ArgumentNullException("stream is null");
            if (!exportBook.Sheets?.Any() ?? true) throw new ArgumentNullException("sheets is empty");

            if (exportBook.FileType == FileType.Csv)
                ExportCsv(exportBook, stream);
            else
                ExportExcel(exportBook, stream);
        }

        public byte[] Export(ExportBook exportBook)
        {
            using var stream = new MemoryStream();
            Export(exportBook, stream);
            return stream.ToArray();
        }

        private static void ExportCsv(ExportBook exportBook, Stream stream)
        {
            var sheetWrapper = ExportSheetWrapperBuilder.BuildSheetWrapper(exportBook.Sheets.First());
            if (sheetWrapper == null) return;

            var csvRows = sheetWrapper.Rows.Select(row => new CsvRow(row.RowIndex, sheetWrapper.Columns.Count, row.Cells.Select(x => new CsvCell(x.ColumnIndex, x.Value)))).ToList();
            var csvSheet = new CsvSheet(csvRows);
            csvSheet.Write(stream);
        }

        private async void ExportExcel(ExportBook exportBook, Stream stream)
        {
            var workBook = WorkbookHelper.CreateWorkbook(exportBook.FileType);
            try
            {
                var sheetNameCount = new Dictionary<string, int>();
                foreach (var exportBookSheet in exportBook.Sheets!)
                {
                    //当前sheet用名字命名，并且已经生成过同名，说明是合并的。如果没有指定起始行号，则行号追加
                    if (!exportBookSheet.StartRowIndex.HasValue && exportBookSheet.SheetName.IsNotNullOrWhiteSpace() && sheetNameCount.ContainsKey(exportBookSheet.SheetName))
                        exportBookSheet.StartRowIndex = sheetNameCount[exportBookSheet.SheetName];

                    var sheetWrapper = ExportSheetWrapperBuilder.BuildSheetWrapper(exportBookSheet);
                    if (sheetWrapper == null) continue;

                    //同名sheet行号记录
                    if (exportBookSheet.SheetName.IsNotNullOrWhiteSpace())
                    {
                        if (sheetNameCount.ContainsKey(exportBookSheet.SheetName))
                            sheetNameCount[exportBookSheet.SheetName] = sheetNameCount[exportBookSheet.SheetName] + sheetWrapper.Rows.Count;
                        else
                            sheetNameCount.Add(exportBookSheet.SheetName, sheetWrapper.Rows.Count);
                    }

                    var sheet = exportBookSheet.SheetName.IsNullOrWhiteSpace() ? workBook.CreateSheet() : workBook.GetSheet(exportBookSheet.SheetName) ?? workBook.CreateSheet(exportBookSheet.SheetName);

                    FillSheet(workBook, sheet, sheetWrapper);
                }
                workBook.Write(stream);
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                workBook?.Close();
            }
        }

        private void FillSheet(IWorkbook workBook, ISheet sheet, ExportSheetWrapper sheetWrapper)
        {
            var cacheCellStyles = new ConcurrentDictionary<string, ICellStyle>();
            foreach (var row in sheetWrapper.Rows)
            {
                var excelRow = sheet.CreateRow(row.RowIndex);
                foreach (var cell in row.Cells)
                {
                    var excelCell = excelRow.CreateCell(cell.ColumnIndex);
                    excelCell.SetCellValue(cell.Value);

                    if (cell.CellStyle != null)
                    {
                        excelCell.CellStyle = cacheCellStyles.GetOrAdd(cell.CellStyle.GetCacheKey(), (key) =>
                        {
                            return cell.CellStyle.ToCellStyle(workBook);
                        });
                    }
                }
            }

            Merged(sheetWrapper.Rows, sheet);

            SetColumnWidth(sheet, sheetWrapper.Columns);

            if (workBook is XSSFWorkbook && sheetWrapper.WaterMark != null)
                SetWatermark((XSSFWorkbook)workBook, sheet, sheetWrapper.WaterMark);

            static void SetColumnWidth(ISheet sheet, List<ExportColumnWrapper> columns)
            {
                foreach (var item in columns)
                {
                    sheet.AutoSizeColumn(item.ColumnIndex);
                    if (!item.MinWidth.HasValue && !item.MaxWidth.HasValue)
                        continue;

                    var width = (int)(sheet.GetColumnWidth(item.ColumnIndex) * 1.2);

                    if (item.MinWidth.HasValue && width < item.MinWidth)
                    {
                        width = item.MinWidth.Value;
                        sheet.SetColumnWidth(item.ColumnIndex, width);
                    }
                    else if (item.MaxWidth.HasValue && width > item.MaxWidth)
                    {
                        width = item.MaxWidth.Value;
                        sheet.SetColumnWidth(item.ColumnIndex, width);
                    }

                }
            }
        }

        private void Merged(List<ExportRowWrapper> listData, ISheet sheet)
        {
            var rowMerges = new Dictionary<int, List<(int BeginRow, int EndRow, string? CompareValue)>>();
            var columnMerges = new List<(int RowIndex, List<List<int>> ColumnIndexs)>();

            foreach (var row in listData)
            {
                var rowMergeColumns = new List<List<int>>();
                foreach (var cell in row.Cells)
                {
                    if (cell.MergeColumn)
                    {
                        //最后一组需要合并的列
                        var lastList = rowMergeColumns.LastOrDefault();
                        if (lastList == null)
                        {
                            rowMergeColumns.Add(new List<int> { cell.ColumnIndex });
                        }
                        else
                        {
                            //最后一个需要合并的列索引
                            var lastColumnIndex = lastList.Last();

                            //如果和当前列是相邻列
                            if (lastColumnIndex == cell.ColumnIndex - 1)
                                lastList.Add(cell.ColumnIndex);
                            //如果不是相邻列，且上一组合并列只有一个列，移除
                            else if (lastList.Count == 1)
                                rowMergeColumns.RemoveAt(rowMergeColumns.Count - 1);
                        }
                    }

                    if (!cell.MergedRowAlone && !cell.MergedRowByPrimaryKey) continue;

                    //判断合并行对比值，主键或者当前列值
                    var compareValue = cell.MergedRowByPrimaryKey ? row.PrimaryKey : cell.Value;

                    //当前列当前行,取到最后一个合并行
                    if (!rowMerges.TryGetValue(cell.ColumnIndex, out List<(int _0, int _1, string? _2)> preDataList))
                    {
                        rowMerges.Add(cell.ColumnIndex, new List<(int BeginRow, int EndRow, string? CompareValue)>() { (row.RowIndex, 0, compareValue) });
                        continue;
                    }

                    var (lastBeginRow, lastEndRow, lastCompareValue) = preDataList.Last();
                    //当前行与上一行相等，重置EndRow
                    if (lastCompareValue == compareValue)
                    {
                        preDataList.RemoveAt(preDataList.Count - 1);
                        preDataList.Add((lastBeginRow, row.RowIndex, lastCompareValue));
                    }
                    else
                    {
                        preDataList.Add((row.RowIndex, 0, compareValue));
                    }
                }
                //上一组合并列只有一个列，移除
                if (rowMergeColumns.Count > 0 && rowMergeColumns.Last().Count() == 1)
                    rowMergeColumns.RemoveAt(rowMergeColumns.Count - 1);


                if (rowMergeColumns.IsNotNullOrEmpty())
                    columnMerges.Add((row.RowIndex, rowMergeColumns));
            }

            foreach (var merge in rowMerges)
            {
                foreach (var (beginRow, endRow, _) in merge.Value.Where(x => x.EndRow > 0))
                    sheet.AddMergedRegion(new CellRangeAddress(beginRow, endRow, merge.Key, merge.Key));
            }

            foreach (var (rowIndex, columnIndexs) in columnMerges)
            {
                foreach (var columns in columnIndexs)
                    sheet.AddMergedRegion(new CellRangeAddress(rowIndex, rowIndex, columns.First(), columns.Last()));
            }
        }




        public void SetWatermark(XSSFWorkbook workBook, ISheet sheet, ExportSheetWarterMark waterMark)
        {
            try
            {
                byte[] bytes = waterMark.ImageData ?? DrawText(waterMark.Content, waterMark.Font, waterMark.TextColor, waterMark.BackColor, waterMark.Width, waterMark.Height);

                int pictureIdx = workBook.AddPicture(bytes, PictureType.PNG);
                var poixmlDocumentPart = (POIXMLDocumentPart)workBook.GetAllPictures()[pictureIdx];

                var sheetWB = (XSSFSheet)sheet;
                var ppn = poixmlDocumentPart.GetPackagePart().PartName;
                var relType = XSSFRelation.IMAGES.Relation;
                var pr = sheetWB.GetPackagePart().AddRelationship(ppn, TargetMode.Internal, relType, null);

                sheetWB.GetCTWorksheet().picture = new NPOI.OpenXmlFormats.Spreadsheet.CT_SheetBackgroundPicture()
                {
                    id = pr.Id
                };
            }
            catch (Exception e)
            {
            }
            //手动绘图
            byte[] DrawText(string text, Font font, Color textColor, Color backColor, double height, double width)
            {
                //创建一个指定宽度和高度的位图图像
                using var img = new Bitmap((int)width, (int)height);
                using Graphics drawing = Graphics.FromImage(img);
                //获取文本大小
                var textSize = drawing.MeasureString(text, font);
                //绘制背景
                drawing.Clear(backColor);
                //创建文本刷
                using var textBrush = new SolidBrush(textColor);

                var ypos = (Math.Sqrt(Math.Pow(width, 2) + Math.Pow(height, 2)) - textSize.Width) / 2;
                drawing.TranslateTransform(0, (float)ypos);
                drawing.RotateTransform(30);//控制倾斜角度
                drawing.DrawString(text, font, textBrush, 10, 0);
                drawing.ResetTransform();
                drawing.Save();
                using var stream = new MemoryStream();
                img.Save(stream, ImageFormat.Png);
                return stream.ToArray();

            }
        }

    }
}
